-- Parsec
-- Module 'scanner'

function string:unilen()
  local _, count = string.gsub(self, "[^\128-\193]", "");
  return count;
end

function string:startsWith(start)
   return string.sub(self,1,string.unilen(start))==start;
end

function string:trimStart(start)
   return string.sub(self,string.len(start)+1);
end

function string:replace(s1,s2)
  return string.gsub(self, s1, s2)
end

function string:splitToArray(pat)
  local arr = self:split(pat);
  local ret = {};
  for v in arr do
    table.insert(ret, v);
  end
  return table;
end

function string:split(pat)
  pat = pat or "%s+";
  local st, g = 1, self:gmatch("()("..pat..")")
  local function getter(segs, seps, sep, cap1, ...)
    st = sep and seps + #sep
    return self:sub(segs, (seps or 0) - 1), cap1 or sep, ...
  end
  return function() 
    if st then 
      return getter(st, g()) 
    end 
  end
end

function string:lines()
  local arr = self:split("^");
  local tab = {};
  local count = 1;  
  for e in arr do
    tab[count] = e;
    count = count + 1;
  end  
  return tab;
end

function string:words()
  local arr = self:split(" ");
  local tab = {};
  local count = 1;  
  for e in arr do
    tab[count] = e;
    count = count + 1;
  end  
  return tab;
end

function string:splitByLen(n)
  local arr = self:split(" ");
  local tab = {};
  local row = "";
  for e in arr do
    row = row..e.." ";
    local ln = row:unilen();
    if ln >= n then
      table.insert(tab, row);
      n = ln;
      row = "";
    end
  end
  if row ~= "" then
    table.insert(tab, row);
  end
  return tab;
end

function string:parse(apply)
  return string.gsub(self, "\\?[\\{}]", { ["{"] = "\001", ["}"] = "\002" })
    :gsub("\001([^\002]+)\002", apply);
end

function string:expand(this)
  return self:parse(function(s) return tostring(apply(s,this)) end);
end